﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Firefly.Framework.Components.Terrain
{
    public class NormalMap : IDisposableEx
    {
        public Vector3[,] NormalStore;

        public Texture2D NormalTexture { get; private set; }
        public Vector3 this[int x, int y]
        {
            get { return NormalStore[x, y]; }
            set { NormalStore[x, y] = value; }
        }

        public NormalMap(GraphicsDevice graphicsDevice, HeightMap heightMap, float verticalScale, float horizontalScale)
        {
            NormalStore = new Vector3[heightMap.Width, heightMap.Height];
            NormalTexture = new Texture2D(graphicsDevice, heightMap.Width, heightMap.Height, 0, TextureUsage.AutoGenerateMipMap, SurfaceFormat.Color);
            GenerateVertexNormals(ref heightMap, verticalScale, horizontalScale);
        }

        private void GenerateVertexNormals(ref HeightMap heightMap, float verticalScale, float horizontalScale)
        {
            Color[] normalData = new Color[heightMap.Width * heightMap.Height];

            for (int y = 0; y < heightMap.Height; y++)
            {
                for (int x = 0; x < heightMap.Width; x++)
                {
                    Vector3 normalX = Vector3.Zero;
                    Vector3 normalY = Vector3.Zero;
                    Vector3 normalVector = new Vector3();

                    if (x > 0 && y > 0 && x < heightMap.Width - 1 && y < heightMap.Height - 1)
                    {
                        normalX = new Vector3((heightMap[x - 1, y] - heightMap[x + 1, y]) / 2 * verticalScale, 0, horizontalScale);
                        normalY = new Vector3(0, (heightMap[x, y - 1] - heightMap[x, y + 1]) / 2 * verticalScale, horizontalScale);
                        normalVector = normalX + normalY;
                        normalVector.Normalize();

                        Vector3 texVector;
                        texVector.X = (normalVector.X + 1) / 2f;
                        texVector.Y = (normalVector.Y + 1) / 2f;
                        texVector.Z = (normalVector.Z + 1) / 2f;
                        normalData[x + (y * heightMap.Height)] = new Color(texVector);
                    }
                    else
                    {
                        normalX = new Vector3(0, 0, horizontalScale);
                        normalY = new Vector3(0, 0, horizontalScale);
                        normalVector = normalX + normalY;
                        normalVector.Normalize();

                        Vector3 texVector;
                        texVector.X = (normalVector.X + 1) / 2f;
                        texVector.Y = (normalVector.Y + 1) / 2f;
                        texVector.Z = (normalVector.Z + 1) / 2f;
                        normalData[x + (y * heightMap.Height)] = new Color(texVector);
                    }

                    NormalStore[x, y] = normalVector;
                }
            }

            GenerateNormalTexture(ref normalData);
        }

        private void GenerateNormalTexture(ref Color[] normalData)
        {
            NormalTexture.SetData<Color>(normalData);
            NormalTexture.GenerateMipMaps(TextureFilter.Anisotropic);
        }

        #region IDisposableEx Members

        public bool Disposed { get; protected set; }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (!Disposed)
            {
                if (disposing)
                {
                    NormalTexture.Dispose();
                    NormalTexture = null;
                }

                Disposed = true;
            }
        }

        ~NormalMap()
        {
            Dispose(false);
        }

        #endregion
    }
}